package top.scsoul.voes.examination.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.transaction.annotation.Transactional;
import top.scsoul.voes.common.constance.*;
import top.scsoul.voes.common.core.domain.model.AnswerSheet;
import top.scsoul.voes.common.core.domain.model.ExameerOnlineVo;
import top.scsoul.voes.common.exception.SystemException;
import top.scsoul.voes.common.utils.ExcelExportDownloadUtil;
import top.scsoul.voes.examination.entity.*;
import top.scsoul.voes.examination.mapper.*;
import top.scsoul.voes.examination.service.ExamRegistrationService;
import top.scsoul.voes.examination.service.ExaminationService;
import org.springframework.stereotype.Service;
import top.scsoul.voes.examination.service.QuestionBankService;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * (Examination)表服务实现类
 *
 * @author guolei
 * @since 2021-07-21 15:37:36
 */
@Transactional
@Slf4j
@Service("examinationService")
public class ExaminationServiceImpl implements ExaminationService {

    @Autowired
    private ExaminationMapper examinationMapper;
    @Resource
    private QuestionBankMapper questionBankMapper;
    @Autowired
    TestPaperMapper testPaperMapper;

    @Autowired
    QuestionBankService queryBankService;

    @Autowired
    ExamRegistrationMapper examRegistrationMapper;

    @Autowired
    ExamRegistrationService examRegistrationService;
    @Autowired
    ScoreMapper scoreMapper;

    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    @Override
    public Examination queryById(Integer id) {
        return this.examinationMapper.queryById(id);
    }

    /**
     * 查询多条数据
     *
     * @param offset 查询起始位置
     * @param limit  查询条数
     * @return 对象列表
     */
    @Override
    public List<Examination> queryAllByLimit(int offset, int limit) {
        return this.examinationMapper.queryAllByLimit(offset, limit);
    }

    @Override
    public PageInfo<Examination> queryAll(Examination examination, int currentPage, int pageSize) {
        PageHelper.startPage(currentPage, pageSize);
        List<Examination> examinations = examinationMapper.queryAll(examination);
        return new PageInfo<Examination>(examinations);
    }

    @Override
    public PageInfo<Examination> queryByUsername(String username, int currentPage, int pageSize) {
        System.out.println("参数");
        System.out.println(username);
        PageHelper.startPage(currentPage, pageSize);
        List<Examination> examinations = examinationMapper.queryByUsername(username);
        return new PageInfo<Examination>(examinations);
    }

    /**
     * 新增数据
     *
     * @param examination 实例对象
     * @return 实例对象
     */
    @Override
    public Examination insert(Examination examination) {
        examination.setExamState(ExaminationState.WAITING_PROPOSITION);
        examination.setExamJoinNum(0);
        examinationMapper.insert(examination);

        /*
         * 添加定时任务
         * 到达考试开始时间，将状态设置为3
         * 到达考试结束时间，将状态设置为4
         */
        Integer id = examination.getId();
        Date examBeginTime = examination.getExamBeginTime();
        Date examOverTime = examination.getExamOverTime();
        examBeginAndOverTimer(id, examBeginTime, examOverTime);


        return examination;
    }

    /**
     * 修改数据
     *
     * @param examination 实例对象
     * @return 实例对象
     */
    @Override
    public Examination update(Examination examination) {
        examinationMapper.update(examination);
        /*
         *   BUG
         *           修改不能添加定时功能
         * 添加定时任务
         * 到达考试开始时间，将状态设置为3
         * 到达考试结束时间，将状态设置为4
         *
         */
//        Integer id = examination.getId();
//        Date examBeginTime = examination.getExamBeginTime();
//        Date examOverTime = examination.getExamOverTime();
//        examBeginAndOverTimer(id, examBeginTime, examOverTime);

        return this.queryById(examination.getId());
    }

    /**
     * 通过主键删除数据
     *
     * @param id 主键
     * @return 是否成功
     */
    @Override
    public boolean deleteById(Integer id) {
        testPaperMapper.deleteByExaminationId(id);
        examRegistrationMapper.deleteByExamId(id);
        scoreMapper.deleteByExamId(id);
        return examinationMapper.deleteById(id) > 0;
    }

    @Override
    public void deleteByIds(List<Integer> ids) {
        testPaperMapper.deleteByExaminationIds(ids);
        examRegistrationMapper.deleteByExamIds(ids);
        scoreMapper.deleteByExamIds(ids);
        examinationMapper.deleteByIds(ids);
    }

    @Override
    public void export(List<Integer> ids, HttpServletResponse response) throws IOException {
        List<Examination> examinations = examinationMapper.queryByIds(ids);
        ExcelExportDownloadUtil.excelExport(response, examinations, Examination.class, "考试信息表");
    }

    /**
     * 随机抽题目
     *
     * @param id            id
     * @param keyWord       关键字
     * @param singleChose   单选个数
     * @param multipleChose 多选
     * @param fillBlanks    填空题
     * @param judge         判断题
     */
    @Override
    public void randomQuestions(Integer id, String keyWord, Long singleChose, Long multipleChose, Long fillBlanks, Long judge, Long subjective) {
        Integer state = examinationMapper.checkState(id);
        if (state > ExaminationState.BEFORE_THE_EXAM) {
            throw new SystemException("开考时间已过，不允许抽题");
        }
        //清除老版电子试卷
        testPaperMapper.deleteByExaminationId(id);

        List<Integer> singleChoseIds = null;
        List<Integer> multipleChoseIds = null;
        List<Integer> fillBlanksIds = null;
        List<Integer> judgeIds = null;
        List<Integer> subjectiveIds = null;
        //拿出题库所有的符合要求的题目
        if (singleChose > 0) {
            singleChoseIds = questionBankMapper.getIdListByTypeAndKeyWord(QuestionType.SINGLE_CHOICE, keyWord);
        }
        if (multipleChose > 0) {
            multipleChoseIds = questionBankMapper.getIdListByTypeAndKeyWord(QuestionType.MULTIPLE_CHOICE, keyWord);
        }

        if (fillBlanks > 0) {
            fillBlanksIds = questionBankMapper.getIdListByTypeAndKeyWord(QuestionType.FILL_IN_THE_BLANKS, keyWord);
        }
        if (judge > 0) {
            judgeIds = questionBankMapper.getIdListByTypeAndKeyWord(QuestionType.JUDGE, keyWord);
        }
        if (subjective > 0) {
            subjectiveIds = questionBankMapper.getIdListByTypeAndKeyWord(QuestionType.SUBJECTIVE, keyWord);
        }
        Random random = new Random();

        //随机抽取
        if (singleChoseIds != null && singleChoseIds.size() > 0 && singleChoseIds.size() >= singleChose) {
            while (singleChoseIds.size() != singleChose) {
                singleChoseIds.remove(random.nextInt(singleChoseIds.size()));
            }
            //组装试卷单选题
            singleChoseIds.forEach(ids -> {
                TestPaper testPaper = new TestPaper();
                testPaper.setExaminationId(id);
                testPaper.setQuestionBankId(ids);
                testPaperMapper.insert(testPaper);
            });
        }

        if (multipleChoseIds != null && multipleChoseIds.size() > 0 && multipleChoseIds.size() >= multipleChose) {
            while (multipleChoseIds.size() != multipleChose) {
                multipleChoseIds.remove(random.nextInt(multipleChoseIds.size()));
            }
            //组装试卷多选题
            multipleChoseIds.forEach(ids -> {
                TestPaper testPaper = new TestPaper();
                testPaper.setExaminationId(id);
                testPaper.setQuestionBankId(ids);
                testPaperMapper.insert(testPaper);
            });
        }

        if (judgeIds != null && judgeIds.size() > 0 && judgeIds.size() >= judge) {
            while (judgeIds.size() != judge) {
                judgeIds.remove(random.nextInt(judgeIds.size()));
            }
            //组装试卷判断题
            judgeIds.forEach(ids -> {
                TestPaper testPaper = new TestPaper();
                testPaper.setExaminationId(id);
                testPaper.setQuestionBankId(ids);
                testPaperMapper.insert(testPaper);
            });
        }

        if (fillBlanksIds != null && fillBlanksIds.size() > 0 && fillBlanksIds.size() >= fillBlanks) {
            while (fillBlanksIds.size() != fillBlanks) {
                fillBlanksIds.remove(random.nextInt(fillBlanksIds.size()));
            }
            //组装试卷填空题
            fillBlanksIds.forEach(ids -> {
                TestPaper testPaper = new TestPaper();
                testPaper.setExaminationId(id);
                testPaper.setQuestionBankId(ids);
                testPaperMapper.insert(testPaper);
            });
        }

        if (subjectiveIds != null && subjectiveIds.size() > 0 && subjectiveIds.size() >= subjective) {
            while (subjectiveIds.size() != subjective) {
                subjectiveIds.remove(random.nextInt(subjectiveIds.size()));
            }
            //组装试卷主观题（填空题）
            subjectiveIds.forEach(ids -> {
                TestPaper testPaper = new TestPaper();
                testPaper.setExaminationId(id);
                testPaper.setQuestionBankId(ids);
                testPaperMapper.insert(testPaper);
            });
        }

        Examination examination = new Examination();
        examination.setId(id);
        examination.setExamState(ExaminationState.BEFORE_THE_EXAM);
        examinationMapper.update(examination);
        log.info("id:{}", id);
        log.info("抽题完成");
        log.info("单选题：{}", singleChoseIds);
        log.info("多选题：{}", multipleChoseIds);
        log.info("判断题：{}", judgeIds);
        log.info("填空选题：{}", fillBlanksIds);
    }

    /**
     * 组装试卷
     *
     * @param examId 考试id
     * @return
     */
    @Override
    public List<QuestionBank> testPaper(Integer examId) {
        return testPaper(examId, true);
    }

    /**
     * 组装试卷
     *
     * @param examId 考试id
     * @return
     */
    @Override
    public List<QuestionBank> testPaper(Integer examId, boolean answer) {
        List<TestPaper> testPapers = testPaperMapper.queryByExamId(examId);
        List<QuestionBank> questionBankArrayList = new ArrayList<>();
        if (testPapers != null && testPapers.size() > 0) {
            testPapers.forEach(item -> {
                questionBankArrayList.add(queryBankService.queryById(item.getQuestionBankId(), answer));
            });
        }
        return questionBankArrayList;
    }

    /**
     * 考生开始答题
     *
     * @param username
     * @param examId
     * @return
     */
    @Override
    public List<QuestionBank> answerSheetTestPaper(String username, Integer examId) {
        Integer integer = examRegistrationMapper.queryByExamIdAndUsername(examId, username);
        if (integer == 0) {
            throw new SystemException("未报名该场考试");
        }

        //查看是否到时间
        Examination examination = examinationMapper.queryById(examId);
        long beginTime = examination.getExamBeginTime().getTime();
        long overTime = examination.getExamOverTime().getTime();
        long now = System.currentTimeMillis();
        //还没有到开考时间
        if (now < beginTime) {
            throw new SystemException("还没有到开考时间");
        }
        //考试结束
        if (now > overTime) {
            throw new SystemException("考试已经结束");
        }
        //考试10分钟后不允许加入考试


        ExamRegistration examRegistration = examRegistrationMapper.queryExamRegistrationByUsernameAndExamId(username, examId);
        Integer state = examRegistration.getState();

        if (state.equals(ExamRegistrationState.SUBMIT)) {
            throw new SystemException("你已参加过该场考试！");
        }


        examRegistration.setState(ExamRegistrationState.JOIN);
        examRegistrationMapper.update(examRegistration);

        //组装试卷 不需要答案
        return testPaper(examId, false);
    }

    @Async
    @Override
    public void submitAnswerSheet(Integer examId, String username, List<AnswerSheet> answerSheet) {
        long now = new Date().getTime();
        //看看状态是不是 考试中
        Examination examination = examinationMapper.queryById(examId);
        long time0 = examination.getExamBeginTime().getTime();
        long time1 = examination.getExamOverTime().getTime();
        /*
         * 10秒的网络延迟等待
         */
        if (now < time0 || now > time1 + 10000) {
            throw new SystemException("请在规定的时间内交卷");
        }


        //1.查询验证该用户的准考信息
        ExamRegistration examRegistration = examRegistrationMapper.queryExamRegistrationByUsernameAndExamId(username, examId);

        //正常情况下是有的
        if (examRegistration == null) {
            throw new SystemException("无该考生报名信息，非法请求");
        }

        if (!examRegistration.getState().equals(ExamRegistrationState.JOIN)) {
            throw new SystemException("状态异常，非法请求");
        }
        examRegistration.setState(ExamRegistrationState.SUBMIT);
        examRegistrationMapper.update(examRegistration);
        //2.获取试卷
        List<QuestionBank> questionBanks = this.testPaper(examId, true);
        //正常情况下是有的
        if (questionBanks == null || questionBanks.size() == 0) {
            throw new SystemException("非法请求");
        }
        //正常情况下是有的
        if (answerSheet == null || answerSheet.size() == 0) {
            throw new SystemException("非法请求");
        }

        //3.自动阅卷


        DecimalFormat dF = new DecimalFormat("0.0");
        //每题的分数
        Float everyItemScore = Float.parseFloat(dF.format((float) 100 / questionBanks.size()));

        for (int i = 0; i < questionBanks.size(); i++) {
            //记录到数据库
            Score score = new Score();

            QuestionBank questionItem = questionBanks.get(i);
            AnswerSheet answerSheetItem = answerSheet.get(i);
            //获取题的ID
            Integer id = questionItem.getId();
            //获取考试答案(非多选题)
            String answer = answerSheetItem.getRightKey();
            //获取题型
            Integer type = questionItem.getType();

            boolean answerIsTrue = false;

            if (QuestionType.SINGLE_CHOICE.equals(type)) {
                //如果是单选
                QuestionOption questionOption = questionItem.getQuestionOption();
                String rightKey = questionOption.getRightKey();
                if (rightKey.equals(answer)) {
                    //答对了
                    log.info("第{}答对了", i);
                    answerIsTrue = true;
                } else {
                    //答错了
                    score.setExameeErrorAnser(answer);
                    log.info("第{}答错了", i);
                }

            } else if (QuestionType.MULTIPLE_CHOICE.equals(type)) {
                //如果是多选
                QuestionOption questionOption = questionItem.getQuestionOption();
                //abc
                String rightKey = questionOption.getRightKey();
                if (rightKey.equals(answer)) {
                    //答对了
                    log.info("第{}答对了", i);
                    answerIsTrue = true;

                } else {
                    //答错了
                    score.setExameeErrorAnser(answer);
                    log.info("第{}答错了", i);
                }
            } else if (QuestionType.FILL_IN_THE_BLANKS.equals(type)) {
                //如果是填空
                QuestionFillBlanks questionFillBlank = questionItem.getQuestionFillBlank();
                String rightKey = questionFillBlank.getAnswer();

                rightKey = rightKey.trim();
                if (answer != null) {
                    answer = answer.trim();
                }

                if (rightKey.equals(answer)) {
                    //答对了
                    log.info("第{}答对了", i);
                    answerIsTrue = true;

                } else {
                    //答错了
                    score.setExameeErrorAnser(answer);
                    log.info("第{}答错了", i);
                }


            } else if (QuestionType.JUDGE.equals(type)) {
                //如果是判断
                QuestionJudgment questionJudgment = questionItem.getQuestionJudgment();

                // T or F
                String rightKey = questionJudgment.getRightKey();

                if (rightKey.equals(answer)) {
                    //答对了
                    log.info("第{}答对了", i);
                    answerIsTrue = true;

                } else {
                    //答错了
                    score.setExameeErrorAnser(answer);
                    log.info("第{}答错了", i);
                }


            } else if (QuestionType.SUBJECTIVE.equals(type)) {
                //如果是主观
                log.info("主观题{}", answer);
            }


            score.setExamId(examId);
            score.setUsername(username);
            score.setQuestionBankId(questionItem.getId());
            score.setQuestionNum(i + 1);

            //设置对错
            if (answerIsTrue) {
                score.setTrueFalse(AnswerSate.IS_TRUE);
                //设置分数
                score.setScore(everyItemScore);
            } else {
                if (QuestionType.SUBJECTIVE.equals(type)) {
                    //如果是主观
                    score.setTrueFalse(AnswerSate.WAIT);
                    //分数由后期管理员打分
                } else {
                    score.setTrueFalse(AnswerSate.IS_FALSE);
                }
            }
            scoreMapper.insert(score);
        }
    }

    @Override
    public List<ExamRegistration> getOnlineExameeInfo(Integer examId) {

        List<ExamRegistration> examRegistrations = examRegistrationMapper.queryByExamId(examId);
        return examRegistrations;
    }

    @Override
    public Integer checkState(Integer examId) {
        return examinationMapper.checkState(examId);
    }


    //存放心跳数据
    volatile Map<String, ExameerOnlineVo> map = new ConcurrentHashMap<>();

    public void exameeOnliner(ExameerOnlineVo exameerOnlineVo) {
        String examCardNum = exameerOnlineVo.getExamCardNum();
        exameerOnlineVo.setDate(new Date());
        map.put(examCardNum, exameerOnlineVo);
    }

    @Override
    public Map<String, Object> getOnlineExameeHeat(Integer examId) {

        Integer examState = checkState(examId);
        List<String> examCardNums = examRegistrationService.queryExamCardNumByExamId(examId);
        List<ExameerOnlineVo> online = new ArrayList<>();
        examCardNums.forEach(examCardNum -> {
            ExameerOnlineVo exameerOnlineVo = map.get(examCardNum);
            if (exameerOnlineVo != null) {
                long time = exameerOnlineVo.getDate().getTime();
                long now = System.currentTimeMillis();
                if (now > (time + 5000)) {
                    exameerOnlineVo.setOnline(OnlineState.DROPPED);
                }
            }
            online.add(exameerOnlineVo);
        });

        HashMap<String, Object> map = new HashMap<>();

        map.put("online", online);
        map.put("examState", examState);
        return map;
    }

    /**
     * 定时任务，非业务
     *
     * @param id
     * @param examBeginTime
     * @param examOverTime
     */
    public void examBeginAndOverTimer(Integer id, Date examBeginTime, Date examOverTime) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                log.info("{}考试开始", id);
                Examination examination = new Examination();
                examination.setId(id);
                examination.setExamState(ExaminationState.IN_THE_EXAM);
                examinationMapper.update(examination);
            }
        }, examBeginTime);

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                log.info("{}考试结束", id);
                Examination examination = new Examination();
                examination.setId(id);
                examination.setExamState(ExaminationState.END_OF_EXAM);
                examinationMapper.update(examination);
                //释放map监考数据
                List<String> examCardNums = examRegistrationMapper.queryAllExamCardNumByExamId(id);
                if (examCardNums != null) {
                    examCardNums.forEach(examCardNum -> {
                        map.remove(examCardNum);
                    });
                }
            }
        }, examOverTime);


    }
}